package com.stxx.assistant.module.ffmpeg;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.stxx.assistant.domain.SysTask;
import com.stxx.assistant.module.JobService;
import com.stxx.assistant.service.SysConfigService;
import lombok.Cleanup;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author Fuzhenlei
 * @date 2025/02/19 12:53
 */
@Slf4j
@Component
public class VideoRecord implements JobService {
    private static final Integer min_second = 10;
    private static final Integer max_size = 10;
    @Inject
    SysConfigService configService;
    @Override
    public void exec(SysTask task) throws IOException, InterruptedException {
        log.info("︵︵︵︵︵︵︵︵task start︵︵︵︵︵︵︵︵︵︵");
        Params params = task.paramsCast(Params.class);
        List<RtspInfo> rtspList = params.rtspList();

        rtspList.forEach(rtsp -> {
            log.info("rtsp:" + rtsp);
        });
        String ffmpegPath = configService.config(SysConfigService.ffmpeg_path, String.class);
        String filePath = configService.config(SysConfigService.task_file_path, String.class);
        String prefix = DigestUtil.md5Hex(params.getRtsp());
        List<String> fileList = new ArrayList<>();
        List<Thread> threadList = new ArrayList<>();
        AtomicReference<Boolean> success = new AtomicReference<>(true);
        for (int i = 0; i < rtspList.size(); i++){
            int finalI = i;
            Thread thread = new Thread(() -> {
                try {
                    String fileName =  prefix + "_" + finalI;
                    String allPath =fileName;
                    RtspInfo info = rtspList.get(finalI);
                    List<String> command = Arrays.asList(ffmpegPath + "/ffmpeg", "-i", "\"" + info.getRtsp() + "\"", "-t" ,info.getTime()+"", "-c:v", "copy", "-c:a", "aac",allPath , "-y");
                    ffmpegExec(command,info.getTime()+10);
                    fileList.add(allPath);
                } catch (Exception e) {
                    success.set(false);
                    e.printStackTrace();
                }
            });
            thread.start();
            threadList.add(thread);
        }
        try {
            for(Thread thread:threadList){
                thread.join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //录制片段成功
        if(success.get()){
            String concatFilePath =Paths.get(filePath, prefix+".txt").toString();
            try (FileWriter writer = new FileWriter(concatFilePath)) {
                String content = fileList.stream().sorted().map(file -> {
                    return "file '" + file + "'";
                }).collect(Collectors.joining("\n"));
                writer.write(content);
            } catch (IOException e) {
                e.printStackTrace();
            }
            //合并文件
            List<String> command = Arrays.asList(ffmpegPath + "/ffmpeg", "-f","concat","-i", "\"" + concatFilePath + "\"", "-c", "copy",Paths.get(filePath, prefix+".mp4").toString() , "-y");
            ffmpegExec(command,600);
            Files.delete(Paths.get(concatFilePath));
            return;
        }

        fileList.forEach(file -> {
            try {
                Files.delete(Paths.get(file));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
        if(success.get()){
            log.info("︶︶︶︶︶︶︶︶︶task success︶︶︶︶︶︶︶︶︶");
        }else{
            log.info("︶︶︶︶︶︶︶︶︶task fail︶︶︶︶︶︶︶︶︶");
            throw new RemoteException("视频录制失败");
        }

    }

    public void ffmpegExec(List<String> command,Integer timeout) throws IOException, InterruptedException {
        ProcessBuilder builder = new ProcessBuilder(command);
        String filePath = configService.config(SysConfigService.task_file_path, String.class);
        builder.directory(new File(filePath));
        builder = builder.redirectErrorStream(true);
        Process process = builder.start();
        @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        while ((line = reader.readLine()) != null) {
            log.info(line);
        }
        process.waitFor(timeout, TimeUnit.SECONDS);
    }

    public static void main(String[] args) {
        Params p = new Params();
        p.setRtsp("rtsp://admin:hnstxx@2023@192.168.100.140:443/rtsp/playback?channel=1&subtype=0&recordtype=1&starttime=2025-2-18T01:57:39Z&endtime=2025-2-18T02:00:39Z");
        p.setTimeFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        List<RtspInfo> r = p.rtspList();
        r.forEach(i->{
            System.out.println(i.getRtsp()+"----"+i.getTime());
        });

    }


    @Data
    public static class Params{
        private String rtsp;
        private String timeFormat;
        /**
         * 录制时长 单位秒
         */
        public List<RtspInfo> rtspList(){
            List<RtspInfo> rtspList = new ArrayList<>();
            String f = this.timeFormat;
            f = f.replace("yyyy","\\d{4}");
            f = f.replace("MM","\\d{1,2}");
            f = f.replace("dd","\\d{1,2}");
            f = f.replace("HH","\\d{2}");
            f = f.replace("mm","\\d{2}");
            f = f.replace("ss","\\d{2}");
            f = f.replace("'","");
            Pattern p = Pattern.compile(f);
            Matcher matcher2 = p.matcher(this.rtsp);
            String startTimeStr =null;
            String endTimeStr =null;
            Date start = null;
            Date end = null;
            while (matcher2.find()){
                if(start==null){
                    startTimeStr = matcher2.group();
                    start = DateUtil.parse(startTimeStr,this.timeFormat);
                }else {
                    Date temp = DateUtil.parse(matcher2.group(),this.timeFormat);
                    if(temp.compareTo(start)>0){
                        end = temp;
                        endTimeStr = matcher2.group();
                    }else{
                        end = start;
                        start = temp;
                        endTimeStr = startTimeStr;
                        startTimeStr = matcher2.group();
                    }
                }
            }
            int seconds = (int)DateUtil.between(start,end, DateUnit.SECOND);
            int size = min_second;
            int n = 1;
            if(seconds<min_second){
                size = seconds;
                n = 1;
            } else{
                n = (int)(seconds/min_second);
                if(n>max_size){
                    n=max_size;
                }
                size = seconds/n;
            }
            //System.out.println("N:"+n+"----size:"+size);
            String newEndTimeStr = DateUtil.format(DateUtil.offsetSecond(end,5),this.timeFormat);
            for(int i=0;i<n;i++){
                System.out.println(i);
                Date s = DateUtil.offsetSecond(start,i*size);
                Date e = DateUtil.offsetSecond(s,size);
                RtspInfo info = new RtspInfo();
                info.setRtsp(this.rtsp.replace(startTimeStr,DateUtil.format(s,this.timeFormat)).replace(endTimeStr,newEndTimeStr));
                if(i==n-1){
                    info.setTime(seconds - (n-1)*size);
                }else{
                    info.setTime(size);
                }
                rtspList.add(info);
            }
            return rtspList;
        }
    }

    @Data
    public static class RtspInfo{
        private String rtsp;
        Integer time;
    }
}
